<template>
	<view class="zv_banner" @touchstart="touchStart" @touchmove="touchMove" @touchend="touchEnd">
		<view class="zv_banner_content" :style="scrollOffsetStyle">
			<!-- 徽章项 - 循环渲染实现首尾相连 -->
			<view v-for="(v,index) in displayItems" :key="'item-' + index"
				:style="(index == displayCurIndex) ? itemParentSelectStyle : itemParentNomalStyle"
				@click="change(v, index)" class="zv_banner_c" :class="{'item-checked': index == displayCurIndex}">
				<view class="zv_banner_img" style="width: 100%;height: 100%;" :style="itemAnimateStyle">
					<image :src="v.imgUrl" mode="aspectFill" class="w100 h100 radius-15"
						style="width: 100%;height: 100%;border-radius: 15rpx;"></image>
				</view>
			</view>
		</view>
		<!-- 弧形背景图 -->
		<image src="/static/user-badge/header-back.png" mode="aspectFill" class="arc-bg"></image>
	</view>
</template>

<script>
	export default {
		name: 'zvMbanner',
		props: {
			// 动画时间
			aTime: {
				type: [Number],
				default: 0.4
			},
			items: {
				type: Array,
				default: null
			},
			//选中的item 是否有放大效果
			itemSelectBig: {
				type: [Boolean],
				default: true
			},
			//是否禁止触摸滑动
			disableTouch: {
				type: [Boolean],
				default: false
			},
			// 默认展示的索引下标
			select: {
				type: Number,
				default: 0
			}
		},
		watch: {
			select(newVal, oldVal) {
				// 总是更新当前索引
				this.curIndex = newVal;
				// 非触摸状态下更新显示
				if (!this.isTouch) {
					// 使用最短路径算法更新显示索引
					const totalItems = this.items.length;
					const targetActualIndex = newVal;

					// 计算三个可能的位置
					const middleGroupIndex = totalItems + targetActualIndex;
					const prevGroupIndex = targetActualIndex;
					const nextGroupIndex = 2 * totalItems + targetActualIndex;

					// 计算最短距离
					const distanceToMiddle = Math.abs(middleGroupIndex - this.displayCurIndex);
					const distanceToPrev = Math.abs(prevGroupIndex - this.displayCurIndex);
					const distanceToNext = Math.abs(nextGroupIndex - this.displayCurIndex);

					// 选择最短路径
					if (distanceToPrev <= distanceToMiddle && distanceToPrev <= distanceToNext) {
						this.displayCurIndex = prevGroupIndex;
					} else if (distanceToNext <= distanceToMiddle && distanceToNext <= distanceToPrev) {
						this.displayCurIndex = nextGroupIndex;
					} else {
						this.displayCurIndex = middleGroupIndex;
					}

					this.scrolloffset = this.startOffset();
				}
			},
			items: {
				handler(newVal, oldVal) {
					this.$nextTick(() => {
						this.scrolloffset = this.startOffset();
					});
				},
				deep: true,
				immediate: true // 添加这个属性
			}
		},
		data() {
			return {
				scrolloffset: 0,
				aniTime: this.aTime,
				isTouch: false,
				isMove: false,
				startX: 0,
				endX: 0,
				curIndex: this.select, // 实际索引
				// displayCurIndex: this.select + this.items.length, // 显示索引（在循环列表中的位置）
				displayCurIndex: 0, // 显示索引（在循环列表中的位置）
				oldOffset: 0
			}
		},
		mounted() {
			// 延迟一帧再初始化，避免初始滚动
			setTimeout(() => {
				if (this.items && this.items.length > 0) {
					this.displayCurIndex = this.select + this.items.length;
				}
				// 使用0动画时间初始化位置
				const originalAniTime = this.aniTime;
				this.aniTime = 0;
				this.scrolloffset = this.startOffset();
				this.$nextTick(() => {
					this.aniTime = originalAniTime;
				});
			}, 0);
			this.scrolloffset = this.startOffset();
		},
		computed: {
			// 计算每个item的宽度
			itemWidth() {
				return 240; // 每个图片250rpx宽
			},
			itemHeight() {
				return this.itemWidth; // 正方形
			},
			// 构造循环显示的项目列表
			displayItems() {
				if (!this.items || this.items.length === 0) return [];

				// 创建一个包含3倍数量项目的数组，实现循环效果
				const displayItems = [];
				const repeatTimes = 3; // 重复3次以确保足够滚动

				for (let i = 0; i < repeatTimes; i++) {
					displayItems.push(...this.items);
				}

				return displayItems;
			},
			// 初始偏移量
			scrollOffsetStyle() {
				// 注意：translateX 的单位得用px，不然会有误差
				return `transition: all ${ this.isTouch ? 0 :this.aniTime }s linear;transform: translateX(${this.scrolloffset}px);`;
			},
			//选中的item容器（放大1.4倍）
			itemParentSelectStyle() {
				const scale = this.itemSelectBig ? 1.4 : 1;
				const width = this.rpxToPx(this.itemWidth * scale);
				const height = this.rpxToPx(this.itemHeight * scale);
				return `width:${width}px;height:${height}px;transition: all ${this.aniTime}s linear;`;
			},
			//正常情况下的容器
			itemParentNomalStyle() {
				const width = this.rpxToPx(this.itemWidth);
				const height = this.rpxToPx(this.itemHeight);
				return `width:${width}px;height:${height}px;transition: all ${this.aniTime}s linear;`;
			},
			itemAnimateStyle() {
				return `transition: all ${this.aniTime}s linear;`;
			}
		},
		methods: {
			// rpx转px
			rpxToPx(rpx) {
				try {
					const res = uni.getSystemInfoSync();
					return (rpx * res.screenWidth) / 750;
				} catch (e) {
					// 出错时使用默认比例
					return (rpx * 375) / 750; // 以iPhone6为基准
				}
			},
			startOffset() {
				const scale = this.itemSelectBig ? 1.4 : 1;
				// 计算偏移量使选中的项居中显示
				const itemWidthPx = this.rpxToPx(this.itemWidth);
				const screenWidthPx = this.rpxToPx(750);

				// 计算居中位置偏移量
				// 屏幕中心 - (选中项宽度/2 + 左侧项宽度)
				const selectedItemWidth = itemWidthPx * scale;
				const centerPosition = screenWidthPx / 2;

				// 调整偏移量计算，使选中项居中
				return centerPosition - selectedItemWidth / 2 - this.displayCurIndex * itemWidthPx;
			},
			touchStart(e) {
				if (this.disableTouch) return;
				let x = e.changedTouches[0].clientX;

				this.isTouch = true;
				// 记录之前的偏移量
				this.oldOffset = this.scrolloffset;
				this.startX = x;
			},
			touchMove(e) {
				if (this.disableTouch) return;
				this.isMove = true;
				let x = e.changedTouches[0].clientX;
				this.endX = x;
				// 实时更新偏移量
				this.scrolloffset = this.oldOffset + (this.endX - this.startX);
			},
			touchEnd(e) {
				if (this.disableTouch) return;
				let x = e.changedTouches[0].clientX;

				const itemWidthPx = this.rpxToPx(this.itemWidth);
				// 滑动距离判断
				if (Math.abs(this.endX - this.startX) < itemWidthPx / 4) {
					// 滑动距离较小，回到原来位置
					this.scrolloffset = this.startOffset();
				} else if (this.isMove) {
					// 滑动距离较大，计算应该选中的新项
					this.moveItemAfterToCenter();
				}

				this.isTouch = false;
				this.isMove = false;
			},
			//针对触摸后屏幕中心点没有和item的中心对齐，使其对齐
			moveItemAfterToCenter() {
				// 根据偏移量计算应该居中的项
				const itemWidthPx = this.rpxToPx(this.itemWidth);

				// 计算偏移量差异
				let deltaOffset = this.scrolloffset - this.startOffset();
				let deltaIndex = Math.round(-deltaOffset / itemWidthPx);

				// 更新显示索引
				let newDisplayIndex = this.displayCurIndex + deltaIndex;

				// 确保索引在有效范围内
				const totalItems = this.displayItems.length;
				if (newDisplayIndex < 0) {
					newDisplayIndex = 0;
				} else if (newDisplayIndex >= totalItems) {
					newDisplayIndex = totalItems - 1;
				}

				// 计算实际索引（映射回原始数组）
				let newActualIndex = newDisplayIndex % this.items.length;
				if (newActualIndex < 0) {
					newActualIndex += this.items.length;
				}

				this.displayCurIndex = newDisplayIndex;
				this.curIndex = newActualIndex;
				this.scrolloffset = this.startOffset();
				this.$emit('change', this.curIndex);

				// 检查是否需要调整位置以保持循环效果
				this.checkAndAdjustPosition();
			},
			change(v, index) {
				// 如果点击的是当前选中的项，不处理
				if (index === this.displayCurIndex) {
					return;
				}

				const totalItems = this.items.length;

				// 如果只有一项，直接返回
				if (totalItems <= 1) {
					return;
				}

				// 计算实际索引
				const actualIndex = index % totalItems;

				// 计算最短路径移动
				// 获取三个可能的显示索引：当前组、前一组、后一组中相同实际索引的位置
				const currentGroupDisplayIndex = totalItems + actualIndex; // 中间组
				const prevGroupDisplayIndex = actualIndex; // 前一组
				const nextGroupDisplayIndex = 2 * totalItems + actualIndex; // 后一组

				// 计算到这三个位置的距离
				const distanceToCurrent = Math.abs(currentGroupDisplayIndex - this.displayCurIndex);
				const distanceToPrev = Math.abs(prevGroupDisplayIndex - this.displayCurIndex);
				const distanceToNext = Math.abs(nextGroupDisplayIndex - this.displayCurIndex);

				// 选择距离最短的索引
				let targetDisplayIndex;
				if (distanceToPrev <= distanceToCurrent && distanceToPrev <= distanceToNext) {
					targetDisplayIndex = prevGroupDisplayIndex;
				} else if (distanceToNext <= distanceToCurrent && distanceToNext <= distanceToPrev) {
					targetDisplayIndex = nextGroupDisplayIndex;
				} else {
					targetDisplayIndex = currentGroupDisplayIndex;
				}

				// 更新索引
				this.curIndex = actualIndex;
				this.displayCurIndex = targetDisplayIndex;
				this.scrolloffset = this.startOffset();
				this.$emit('change', this.curIndex);

				// 检查是否需要调整位置以保持循环效果
				this.checkAndAdjustPosition();
			},
			// 检查并调整位置以保持循环效果
			checkAndAdjustPosition() {
				const groupSize = this.items.length;
				const currentGroup = Math.floor(this.displayCurIndex / groupSize);

				// 如果在第一组或最后一组，调整到中间组以保持循环效果
				if (currentGroup === 0 || currentGroup === 2) {
					// 使用setTimeout避免过渡动画冲突
					setTimeout(() => {
						if (!this.isTouch) {
							const middleGroupStart = groupSize;
							const actualIndex = this.displayCurIndex % groupSize;
							this.displayCurIndex = middleGroupStart + actualIndex;
							// 瞬间调整位置，不带动画
							const originalTransition = this.aniTime;
							this.aniTime = 0;
							this.scrolloffset = this.startOffset();
							this.$nextTick(() => {
								// 恢复动画时间
								this.aniTime = originalTransition;
							});
						}
					}, this.aTime * 1000);
				}
			}
		}
	}
</script>

<style lang="scss">
	view,
	image,
	div {
		box-sizing: border-box;
	}

	.zv_banner {
		position: relative;
		width: 750rpx; // 标准屏幕宽度
		height: 300rpx;
		margin: 0 auto;
		overflow: hidden;

		.zv_banner_content {
			width: max-content;
			display: flex;
			align-items: center;
			height: 300rpx;
		}

		.arc-bg {
			position: absolute;
			bottom: 0;
			left: 0;
			width: 100%;
			height: 100%;
			z-index: 1;
		}

		.zv_banner_content {
			position: relative;
			z-index: 2;
		}
	}

	.zv_banner_c {
		display: flex;
		align-items: center;
		justify-content: center;
		flex-shrink: 0;
	}

	.item-checked {
		// 可以添加选中状态的额外样式
	}
</style>